using System;
using System.Text;

namespace csammisrun.OscarLib.Utility
{
	/// <summary>
	/// Provides static methods for processing messages in SNAC family 0x0013 -- SSI service
	/// </summary>
	internal static class SNAC13
	{
    /// <summary>
		/// Sends a request for parameter information -- SNAC(13,02)
		/// </summary>
		/// <param name="sess">A <see cref="Session"/> object</param>
		public static void RequestParametersList(Session sess)
		{
			SNACHeader sh = new SNACHeader();
			sh.FamilyServiceID = (ushort)SNACFamily.SSIService;
			sh.FamilySubtypeID = (ushort)SSIService.ServiceParametersRequest;
			sh.Flags = 0x0000;
			sh.RequestID = sess.GetNextRequestID();

			DataPacket dp = Marshal.BuildDataPacket(sess, sh, null);
			SNACFunctions.BuildFLAP(dp);
		}

		/// <summary>
		/// Processes the parameter information sent by the server -- SNAC(13,03)
		/// </summary>
		/// <param name="dp">A <see cref="DataPacket"/> object with a buffer containing SNAC(13,03)</param>
		public static void ProcessParametersList(DataPacket dp)
		{
			byte[] SNACData = dp.Data;

			int index = 0;
			ushort[] maximums = null;
			while(index < SNACData.Length)
			{
				ushort key = Marshal.ByteArrayToUshort(SNACData, ref index);
				ushort keylength = Marshal.ByteArrayToUshort(SNACData, ref index);
				switch(key)
				{
					case 0x0004:	// List of maximums
						maximums = new ushort[keylength / 2];
						for(int i = 0; i < keylength / 2; i++)
						{
							maximums[i] = Marshal.ByteArrayToUshort(SNACData, ref index);
						}
						break;
					default:
						index += keylength;
						break;
				}
			}

			// Do something with these capabilities
      dp.ParentSession.Limits.MaxBuddies = maximums[0];
      dp.ParentSession.Limits.MaxGroups = maximums[1];
			dp.ParentSession.Limits.MaxPermits = maximums[2];
			dp.ParentSession.Limits.MaxDenys = maximums[3];

			dp.ParentSession.ParameterSetArrived();
		}

		/// <summary>
		/// Sends a request for the original SSI contact list -- SNAC(13,04)
		/// </summary>
		/// <param name="sess">A <see cref="Session"/> object</param>
		public static void RequestInitialContactList(Session sess)
		{
			SNACHeader sh = new SNACHeader();
			sh.FamilyServiceID = (ushort)SNACFamily.SSIService;
			sh.FamilySubtypeID = (ushort)SSIService.ContactListInitialRequest;
			sh.Flags = 0x0000;
			sh.RequestID = sess.GetNextRequestID();

			DataPacket dp = Marshal.BuildDataPacket(sess, sh, null);
			SNACFunctions.BuildFLAP(dp);
		}

		/// <summary>
		/// Processes the server-stored buddy list -- SNAC(13,06)
		/// </summary>
		/// <param name="dp">A <see cref="DataPacket"/> object with a buffer containing SNAC(13,06)</param>
		public static void ProcessBuddyList(DataPacket dp)
		{
			byte[] SNACData = dp.Data;

			int index = 0;
			byte SSI_version = SNACData[index++];
			ushort num_items = Marshal.ByteArrayToUshort(SNACData, ref index);
			SSIItem item = null;

			for(int i = 0; i < num_items; i++)
			{
				item = Marshal.GetSSIItem(SNACData, ref index);
				dp.ParentSession.SSI.AddSSIItem(item);
			}

			uint last_modified_time = Marshal.ByteArrayToUint(SNACData, ref index);


      // Thanks to smartmlp for noticing this!
      // The buddy list can be sent in multiple SNACs, but only the last
      // in the series will have its LSB set to zero. Don't trigger other
      // events until that happens.
      if ((dp.SNAC.Flags & 0x0001) == 0x0000)
      {
        dp.ParentSession.OnLoginStatusUpdate("Contact list received", 1.0);
        // Send a couple last SNACs, then tell the server that we're ready to go
        SNAC01.SendReadyNotification(dp);
        dp.ParentSession.OnContactListFinished();
      }
		}

		/// <summary>
		/// Requests that the server activate the client's SSI information -- SNAC(13,07)
		/// </summary>
		/// <param name="sess">A <see cref="Session"/> object</param>
		/// <remarks>
		/// Sending this SNAC will cause the server to begin broadcasting online notifications
		/// to the contacts on the client's buddy list.
		/// </remarks>
		public static void ActivateSSI(Session sess)
		{
			SNACHeader sh = new SNACHeader();
			sh.FamilyServiceID = (ushort)SNACFamily.SSIService;
			sh.FamilySubtypeID = (ushort)SSIService.LoadContactList;
			sh.Flags = 0x0000;
			sh.RequestID = sess.GetNextRequestID();

			DataPacket dp = Marshal.BuildDataPacket(sess, sh, null);
			SNACFunctions.BuildFLAP(dp);
		}

		/// <summary>
		/// Adds a series of SSI items to the server-side list -- SNAC(13,08)
		/// </summary>
		/// <param name="sess">A <see cref="Session"/> object</param>
		/// <param name="items">An array of <see cref="SSIItem"/> objects</param>
		public static void AddSSIItems(Session sess, SSIItem[] items)
		{
			SNACHeader sh = new SNACHeader();
			sh.FamilyServiceID = (ushort)SNACFamily.SSIService;
			sh.FamilySubtypeID = (ushort)SSIService.SSIEditAddItems;
			sh.Flags = 0x0000;
			sh.RequestID = sess.GetNextRequestID();
			
			byte[] buffer = Marshal.SSIItemsToByteArray(items);

			sess.SSI.OutstandingRequests++;

			// Send the buffer
			DataPacket dp = Marshal.BuildDataPacket(sess, sh, buffer);
			SNACFunctions.BuildFLAP(dp);
		}

    /// <summary>
    /// Processes a notification of SSI items added remotely -- SNAC(13,08)
    /// </summary>
    /// <param name="dp">A <see cref="DataPacket"/> containing SNAC(13,08)</param>
    public static void ProcessItemsAdded(DataPacket dp)
    {
      byte[] SNACData = dp.Data;

      int index = 0;
      //ushort ssiversion = Marshal.ByteArrayToUshort(SNACData, ref index);
      SSIItem newitem = Marshal.GetSSIItem(SNACData, ref index);
      dp.ParentSession.SSI.AddSSIItem(newitem);
    }

		/// <summary>
		/// Modifies a series of SSI items on the server-side list -- SNAC(13,09)
		/// </summary>
		/// <param name="sess">A <see cref="Session"/> object</param>
		/// <param name="items">An array of <see cref="SSIItem"/> objects</param>
		public static void ModifySSIItems(Session sess, SSIItem[] items)
		{
			SNACHeader sh = new SNACHeader();
			sh.FamilyServiceID = (ushort)SNACFamily.SSIService;
			sh.FamilySubtypeID = (ushort)SSIService.SSIEditUpdateGroupHeader;
			sh.Flags = 0x0000;
			sh.RequestID = sess.GetNextRequestID();

      byte[] buffer = Marshal.SSIItemsToByteArray(items);
			sess.SSI.OutstandingRequests++;

			// Send the buffer
			DataPacket dp = Marshal.BuildDataPacket(sess, sh, buffer);
			SNACFunctions.BuildFLAP(dp);
		}

    /// <summary>
    /// Processes a notification of SSI items modified remotely -- SNAC(13,09)
    /// </summary>
    /// <param name="dp">A <see cref="DataPacket"/> containing SNAC(13,09)</param>
    public static void ProcessItemsModified(DataPacket dp)
    {
      byte[] SNACData = dp.Data;

      int index = 0;
      //ushort ssiversion = Marshal.ByteArrayToUshort(SNACData, ref index);
      SSIItem moditem = Marshal.GetSSIItem(SNACData, ref index);
      dp.ParentSession.SSI.UpdateSSIItem(moditem);
    }

		/// <summary>
		/// Removes a series of SSI items from the server-side list -- SNAC(13,0A)
		/// </summary>
		/// <param name="sess"></param>
		/// <param name="items">An array of <see cref="SSIItem"/> objects</param>
		public static void RemoveSSIItems(Session sess, SSIItem[] items)
		{
			SNACHeader sh = new SNACHeader();
			sh.FamilyServiceID = (ushort)SNACFamily.SSIService;
			sh.FamilySubtypeID = (ushort)SSIService.SSIEditRemoveItem;
			sh.Flags = 0x0000;
			sh.RequestID = sess.GetNextRequestID();

			byte[] buffer = Marshal.SSIItemsToByteArray(items);

			sess.SSI.OutstandingRequests++;

			// Send the buffer
			DataPacket dp = Marshal.BuildDataPacket(sess, sh, buffer);
			SNACFunctions.BuildFLAP(dp);
		}

		/// <summary>
		/// Processes the response codes from an SSI update -- SNAC(13,0E)
		/// </summary>
		/// <param name="dp">A <see cref="DataPacket"/> object with a buffer containing SNAC(13,0E)</param>
		public static void ProcessSSIUpdateResponse(DataPacket dp)
		{
			byte[] SNACData = dp.Data;

			ushort[] retvals = new ushort[SNACData.Length / 2];
			for(int i = 0, j = 0; j < SNACData.Length; i++)
			{
				retvals[i] = Marshal.ByteArrayToUshort(SNACData, ref j);
				if(retvals[i] != 0x0000 && retvals[i] != 0x0004)
				{
          ServerErrorCode sec = ServerErrorCode.UnknownError;
          switch (retvals[i])
          {
            case 0x0002:
              sec = ServerErrorCode.SSIItemNotFound;
              break;
            case 0x0003:
              sec = ServerErrorCode.SSIItemAlreadyExists;
              break;
            case 0x000A:
              sec = ServerErrorCode.SSICantAddItem;
              break;
            case 0x000C:
              sec = ServerErrorCode.SSIItemLimitExceeded;
              break;
            case 0x000E:
              sec = ServerErrorCode.SSIItemRequiresAuthorization;
              break;
          }

					dp.ParentSession.OnWarning(sec);
				}
			}

			dp.ParentSession.SSI.OutstandingRequests--;
      Logging.DumpFLAP(new byte[] { }, "Outstanding SSI requests: " + dp.ParentSession.SSI.OutstandingRequests.ToString());
			if(dp.ParentSession.SSI.OutstandingRequests <= 0)
			{
				dp.ParentSession.SSI.OutstandingRequests = 0;
				//SNAC13.SendEditComplete(dp.ParentSession);
        dp.ParentSession.OnSSIEditComplete();
			}
		}

		/// <summary>
		/// Processes the "Local SSI is up to date" message from the server -- SNAC(13,0F)
		/// </summary>
		/// <param name="dp">A <see cref="DataPacket"/> object with a buffer containing SNAC(13,0F)</param>
		public static void ProcessSSIUpToDate(DataPacket dp)
		{
			byte[] SNACData = dp.Data;

			dp.ParentSession.OnContactListFinished();
		}

		/// <summary>
		/// Starts an SSI modification transaction -- SNAC(13,11)
		/// </summary>
		/// <param name="sess">A <see cref="Session"/> object</param>
		public static void SendEditStart(Session sess)
		{
			SNACHeader sh = new SNACHeader();
			sh.FamilyServiceID = (ushort)SNACFamily.SSIService;
			sh.FamilySubtypeID = (ushort)SSIService.ContactsEditStart;
			sh.Flags = 0x0000;
			sh.RequestID = sess.GetNextRequestID();

			DataPacket dp = Marshal.BuildDataPacket(sess, sh, null);
			SNACFunctions.BuildFLAP(dp);
		}

		/// <summary>
		/// Finishes an SSI modification transaction -- SNAC(13,12)
		/// </summary>
		/// <param name="sess">A <see cref="Session"/> object</param>
		public static void SendEditComplete(Session sess)
		{
			SNACHeader sh = new SNACHeader();
			sh.FamilyServiceID = (ushort)SNACFamily.SSIService;
			sh.FamilySubtypeID = (ushort)SSIService.ContactsEditEnd;
			sh.Flags = 0x0000;
			sh.RequestID = sess.GetNextRequestID();

			DataPacket dp = Marshal.BuildDataPacket(sess, sh, null);
			SNACFunctions.BuildFLAP(dp);
		}

		/// <summary>
		/// (Obsolete) Requests that this client be removed from a remote contact list -- SNAC(13,16)
		/// </summary>
		/// <param name="sess">A <see cref="Session"/> object</param>
		/// <param name="screenname">The screenname of the remote contact list</param>
		/// <remarks>
		/// This SNAC is no longer supported by the OSCAR servers, but the functionality is included
		/// in OscarLib for completeness.
		/// </remarks>
		public static void SendRemovalRequest(Session sess, string screenname)
		{
			SNACHeader sh = new SNACHeader();
			sh.FamilyServiceID = (ushort)SNACFamily.SSIService;
			sh.FamilySubtypeID = (ushort)SSIService.RemoveSelfFromContact;
			sh.Flags = 0x0000;
			sh.RequestID = sess.GetNextRequestID();

      Encoding enc = Marshal.ASCII;
      byte screennamelength = (byte)enc.GetByteCount(screenname);

			int index = 0;
			byte[] buffer = new byte[1 + screennamelength];
			buffer[index++] = screennamelength;
			Marshal.InsertString(buffer, screenname, enc, ref index);

			DataPacket dp = Marshal.BuildDataPacket(sess, sh, buffer);
			SNACFunctions.BuildFLAP(dp);
		}

		/// <summary>
		/// Processes a "You Were Added" notification sent by the server -- SNAC(13,1C)
		/// </summary>
		/// <param name="dp">A <see cref="DataPacket"/> object with a buffer containing SNAC(13,1C)</param>
		/// <remarks>
		/// The server only sends this SNAC to ICQ clients. I haven't worked out if it will ever be sent
		/// to an AIM client, but I'm thinking not.
		/// </remarks>
		public static void ProcessAddedMessage(DataPacket dp)
		{
			byte[] SNACData = dp.Data;

			int strlen = (int)SNACData[0];
      string sn = Marshal.ByteArrayToString(SNACData, 1, strlen, Marshal.ASCII);

			dp.ParentSession.OnStatusUpdate(sn + " added you to their contact list");
		}
	}
}
